package com.sangeng.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationSuccessHandler successHandler;

    @Autowired
    private AuthenticationFailureHandler failureHandler;

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //原本方法重写前,也就是父类的代码中,会将UsernamePasswordAuthenticationFilter这个过滤器添加到过滤器链中
        //原本重写前此方法的代码:super.configure(http);这里是在开启表单登录方式,也就是UsernamePasswordAuthenticationFilter这个过滤器拦截/login

        //与我自己写的逻辑作对比序号一样的彼此对比
        //1. 然后从请求参数中提取用户名和密码；
        //2. 封装为 UsernamePasswordAuthenticationToken；
        //3. 提交给 AuthenticationManager 进行认证。
        //4. 认证成功后，将 Authentication 存入 SecurityContext；

        //但是在我们现在的重写后没有调用super.configure(http)方法,而且我想用的是Token 登录机制。
        //所以这里UsernamePasswordAuthenticationFilter这个过滤器并没有被添加在过滤器链里,过滤器链中就不存在UsernamePasswordAuthenticationFilter,
        //而UsernamePasswordAuthenticationFilter过滤器的逻辑就需要我们自己写,
        //比如我登录接口的service里面就模拟了UsernamePasswordAuthenticationFilter这个过滤器的逻辑,

        //1. 用户密码和账号从接口参数传入
        //2. 手动把用户密码和用户名封装进 UsernamePasswordAuthenticationToken
        //3. 把用户信息封装进Authentication中,手动调用AuthenticationManager.authenticate方法进行认证
        //4. 认证成功后返回统一响应结果,因为此时登录请求走完了,后续新的请求过来会在JwtAuthenticationTokenFilter过滤器中完成将Authentication存SecurityContext这一步

        //这4步就是在完成原先UsernamePasswordAuthenticationFilter这个过滤器的逻辑
        //具体可以结合笔记里登录认证过滤器链图片和SanGengTokenDemo中的LoginServiceImpl里login方法的代码来看

        //而这里只要配置了formLogin就会注册UsernamePasswordAuthenticationFilter过滤器到链中,而在另一个项目中,我们自己实现了这个过滤器的逻辑
        //如果你保留 formLogin() 配置，但又在 Service 中手动完成认证逻辑，会出现以下问题：
        //1. 登录请求/login被中过滤器拦截,先经过 UsernamePasswordAuthenticationFilter 做一次认证（可能失败）再通过你的自定义/login接口做第二次认证（成功）
        //2. 若用户名/密码错误，UsernamePasswordAuthenticationFilter 可能直接抛异常或跳转页面，导致后续自定义逻辑无法执行
        //所以要么自定义UsernamePasswordAuthenticationFilter过滤器的逻辑然后不配置,要么配置但不自定义 UsernamePasswordAuthenticationFilter过滤器的逻辑
        http.formLogin()
//                配置认证成功处理器
                .successHandler(successHandler)
//                配置认证失败处理器
                .failureHandler(failureHandler);

        http.logout()
                //配置注销成功处理器
                .logoutSuccessHandler(logoutSuccessHandler);


        //同样因为重写了方法,没有调用父类的配置请求认证的逻辑:http.authorizeRequests((requests) -> ((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated());
        //所以此时接口都不会被 拦截,需要手动配置所有请求都进行认证
        // (讲人话就是原先配置认证哪些接口的配置给我们重写搞没了,得重新自己配置,跟上面一样也是搞没了配置得自己重新配或者写代码替代)
        http.authorizeRequests().anyRequest().authenticated();//配置所有请求都需要认证
    }
}
